// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CaeWssSocket.h"

bool CaeWssSocket::ParseHttpHeader()
{
    uint32_t limit = 4096;
    uint32_t totalLen = 0;
    WebSocketFrameType frameType;
    while (totalLen <= limit && m_rxBuf != nullptr) {
        int ret;
        if (nullptr != m_tlsSocket && m_tlsSocket->GetStatus() == SOCKET_STATUS_NORMAL) {
            ret = m_tlsSocket->Recv(m_rxBuf + totalLen, limit - totalLen);
        } else {
            LOGE("Tls socket status is error.");
            return false;
        }
        if (ret <= 0) {
            LOGE("Socket disconnected when parsing http header.");
            return false;
        }
        totalLen += static_cast<uint32_t>(ret);
        if (m_webSocket != nullptr) {
            frameType = m_webSocket->parseHandshake(m_rxBuf, static_cast<int>(totalLen));
        } else {
            LOGE("Web socket is null.");
            return false;
        }
        if (frameType == INCOMPLETE_FRAME) {
            continue;
        } else {
            return true;
        }
    }
    LOGE("Too many response.");
    return false;
}

CaeWssSocket::CaeWssSocket(CaeTlsSocket *tlsSocket) : CaeWebSocket()
{
    m_tlsSocket = tlsSocket;
}

CaeWssSocket::~CaeWssSocket()
{
    if (m_tlsSocket != nullptr) {
        delete m_tlsSocket;
        m_tlsSocket = nullptr;
    }
}

int CaeWssSocket::GetFd()
{
    if (m_tlsSocket != nullptr) {
        return m_tlsSocket->GetFd();
    } else {
        return -1;
    }
}

void CaeWssSocket::SetSocketOption(uint32_t socketOption)
{
    if (m_tlsSocket != nullptr) {
        m_tlsSocket->SetSocketOption(socketOption);
    }
}

int CaeWssSocket::Recv(void *buf, size_t len)
{
    if (buf == nullptr || len <= 0) {
        LOGE("Invalid parameter in Recv.");
        return 0;
    }
    m_userBuf = (uint8_t *)buf;
    m_userBufLen = len;
    int ret = 0;
    if (m_rxBufLen == 0) {
        if (m_tlsSocket != nullptr && m_tlsSocket->GetStatus() == SOCKET_STATUS_NORMAL) {
            ret = m_tlsSocket->Recv(m_rxBuf, STREAM_MAX_BUF_LEN);
        }
        if (ret <= 0) {
            LOGE("Tls socket status is error.");
            return ret;
        } else {
            m_rxBufLen = static_cast<uint32_t>(ret);
        }
        m_rxBufHead = m_rxBuf;
    }
    int minSize = static_cast<int>((m_rxBufLen < len) ? m_rxBufLen : len);
    int m;
    int readLen = 0;
    while (minSize) {
        if (m_parsingState == WS_PAYLOAD) {
            m = ReadPayload();
            readLen += m;
            minSize -= m;
            if (m_payloadLen == 0) {
                m_parsingState = WS_NEW;
                if (m_opcode == WB_OPC_CLOSE) {
                    LOGE("Client request to disconnect.");
                    DisableSend();
                    SendCloseResponse();
                    this->EventNotice();
                    LOGE("Client request to disconnect out.");
                    return SOCKET_RECV_FAIL_DISCONN;
                }
            }
        }
        if (minSize <= 0) {
            break;
        }
        m = ParseHeader(*(m_rxBufHead)++);
        minSize--;
        m_rxBufLen--;
        if (m < 0) {
            LOGE("Web socket error in ParseHeader.");
            return SOCKET_RECV_FAIL_DISCONN;
        }
    }
    return readLen;
}

int CaeWssSocket::Send(void *uBuf, size_t len)
{
    int ret = 0;
    pthread_mutex_lock(&m_lock);
    if (m_sendFlag) {
        ret = Send(uBuf, len, WB_OPC_BINARY_FRAME);
    }
    pthread_mutex_unlock(&m_lock);
    return ret;
}

int CaeWssSocket::Send(void *uBuf, size_t len, enum WB_OPCODE wp)
{
    int ret = 0;
    if (len > MAX_PAYLOAD) {
        LOGE("Failed to Send buffer, because len > MAX_PAYLOAD.");
        return 0;
    }

    if (m_tlsSocket == nullptr) {
        LOGE("Failed to Send buffer, m_tlsSocket is null.");
        return 0;
    }

    uint32_t frameType = wp;
    frameType |= 1 << 7;
    int size = m_webSocket->makeFrame((WebSocketFrameType)frameType, (uint8_t *)uBuf, (int)len,
                                      m_sendBuf, MAX_PAYLOAD);
    if (m_tlsSocket->GetStatus() == SOCKET_STATUS_NORMAL) {
        ret = m_tlsSocket->Send((uint8_t *)m_sendBuf, size);
    } else {
        LOGE("Tls socket is error.");
    }
    return ret;
}

bool CaeWssSocket::Handshake()
{
    LOGI("Start ParseHttpHeader");
    if (!ParseHttpHeader()) {
        LOGE("Error in parsing http header.");
        return false;
    }

    string answer = m_webSocket->answerHandshake();
    int sendSize;
    if (m_tlsSocket ==  nullptr || m_tlsSocket->GetStatus() != SOCKET_STATUS_NORMAL) {
        LOGE("Tls socket status is error.");
        return false;
    }
    sendSize = m_tlsSocket->Send((void *)answer.c_str(), answer.size());

    if (sendSize != static_cast<int>(answer.size())) {
        LOGE("Send response error.");
        return false;
    }
    LOGD("Handshake success.");
    return true;
}

void CaeWssSocket::CloseSocket()
{
    if (m_tlsSocket != nullptr) {
        m_tlsSocket->CloseSocket();
    }
}